

#include "MyOpengl.h"
#include <fstream>
#include <iostream>
#include <vector>
using namespace::std;

int _width,_height;
int _count = 0 ;
vector<CMatrix> vertexes;
vector<CMatrix> tempvertexes;


CMatrix curMatrix;
vector<CMatrix> matrices ;
GLenum curMode;

void output(){
    _count ++;
    char name[64];
    sprintf(name, "/Users/chenxu/desktop/img%d.ppm",_count);
    ofstream fout;
    int result[500][500]={0};
    fout.open(name);
    fout << "P3\n"<<_width<<" "<<_height<<"\n255\n";
    
    for (int i=0; i<vertexes.size(); i++) {
        
        CMatrix onscreen =   curMatrix * vertexes.at(i);
        //onscreen.Print();
        double dx = onscreen.GetElement(0, 0) ;
        double dy = onscreen.GetElement(1, 0) ;
        double dz = onscreen.GetElement(2, 0) ;
        double temp = onscreen.GetElement(3, 0) ;
        int x = (int)(dx);
        int y = (int)(dy);
        int z = (int)(dz);

//        cout <<"("<< dx << "," << dy << "," << dz<< "," << temp<<")"<<endl;

        if (x<_width && y<_height && x>=0 && y>= 0) {
            result[x][y] = 255;
        }
    }
    
    for (int i = 0; i < _height; i++) {
        for (int j = 0; j < _width; j++) {
            fout << 0  << ' ' << 0<< ' ' << result[j][i] << ' ';
        }
        fout << "\n";
    }
}

void glPopMatrix(){
    curMatrix = matrices.at(matrices.size()-1);
    matrices.pop_back();
}

//save environment
void glPushMatrix(){
   // cout << "push" << endl;
    //curMatrix.Print();
    matrices.push_back(curMatrix);
    
}

void glEnable(GLenum){}
void glTexCoord2f (GLfloat s, GLfloat t){}
void glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz){}


//gather points
void glVertex3f (GLfloat x, GLfloat y, GLfloat z){
    double p[4] = {x,y,z,1};
    CMatrix temp = CMatrix(4,1,p);
    tempvertexes.push_back(temp);
    cout <<"["<< temp.GetElement(0, 0) << "," << temp.GetElement(1, 0) << "," << temp.GetElement(2, 0) <<"]"<<endl;
}

void glBegin (GLenum mode){
    curMode = mode;
    
    tempvertexes.clear();
}

void glEnd (void){
    if (curMode == GL_TRIANGLE_STRIP){
        
    }
    else if (curMode == GL_QUADS){
//        int size = tempvertexes.size();
//        for (int i=0; i<tempvertexes.size(); i++) {
//            
//            CMatrix first =  tempvertexes.at(i);
//            CMatrix second = tempvertexes.at(0);
//            if (i < size-1) {
//                second = tempvertexes.at(i+1);
//            }
//            int x1 = (int)first.GetElement(0, 0);
//            int x2 = (int)second.GetElement(0, 0);
//            int y1 = (int)first.GetElement(1, 0);
//            int y2 = (int)second.GetElement(1, 0);
//            int delta = x2-x1;
//            if (delta > 0) {
//                for (int j = 0; j < delta; j++) {
//                    double x = x1+j;
//                    double y = (y2-y1)* x /(x2-x1) + (x2*y1 - x1*y2)/(x2-x1);
//                    double temp[4] = {x,y,0,1};
//                    CMatrix inter = CMatrix(4,1,temp);
//                    tempvertexes.push_back(inter);
//                }
//            }
//            
//        }
    }
    
    for (int i=0; i<tempvertexes.size(); i++) {
        CMatrix temp = curMatrix * tempvertexes.at(i);
        vertexes.push_back(temp);
    }
}

 void glPolygonMode (GLenum face, GLenum mode){}
 void glBindTexture (GLenum target, GLuint texture){}
 void glDisable (GLenum cap){}

 void glColor3f (GLfloat red, GLfloat green, GLfloat blue){}
 void glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha){}

 void glLightfv (GLenum light, GLenum pname, const GLfloat *params){}
 void glutPostRedisplay(void){}
 void glClearDepth (GLclampf depth){}
 void glGenTextures (GLsizei n, GLuint *textures){}
 void glClear (GLbitfield mask){}
 void glMatrixMode (GLenum mode){}
void glLoadIdentity (void){
    double factor[] =  {1,0,0,0,
                        0,1,0,0,
                        0,0,1,0,
                        0,0,0,1};
    curMatrix = CMatrix(4, 4, factor);
    tempvertexes.clear();
    vertexes.clear();
}
 void glutSwapBuffers(void){}
 void glutMainLoop(void){   output(); }
 void glShadeModel (GLenum mode){}

//recall func
void glutDisplayFunc(void (*func)(void)){
    func();
}
void glutIdleFunc(void (*func)(void)){
    
    while (true) {
        sleep(0.1);
        func();
        output();
    }
}
//system recall
void glutReshapeFunc(void (*func)(int width, int height)) {func(_width,_height);}
void glutKeyboardFunc(void (*func)(unsigned char key, int x, int y)){func('a',0,0);}
void glutMouseFunc(void (*func)(int button, int state, int x, int y)){func(10,0,0,0);}
void glutMotionFunc(void (*func)(int x, int y)){func(0,0);}


//U(User transform)
void glScaled (GLdouble x, GLdouble y, GLdouble z){
    double factor[] = {x,0,0,0,
                       0,y,0,0,
                       0,0,z,0,
                       0,0,0,1};
    CMatrix scale = CMatrix(4,4,factor);
    curMatrix =  curMatrix * scale;
}

void glTranslatef (GLfloat x, GLfloat y, GLfloat z){
    double factor[] = {1,0,0,x,
                       0,1,0,y,
                       0,0,1,z,
                       0,0,0,1};
    CMatrix t = CMatrix(4, 4, factor);
    curMatrix =  curMatrix * t;
}

void glRotated (GLdouble angle, GLdouble x, GLdouble y, GLdouble z){
   
    if (x>0) {
        double factor[] = { 1,0,0,0,
                            0,cos(angle),-sin(angle),0,
                            0,sin(angle),cos(angle),0,
                            0,0,0,1};
        CMatrix t = CMatrix(4, 4, factor);
        curMatrix =  curMatrix * t;
    }

     else if (y>0) {
        double factor[] = { cos(angle),0,sin(angle),0,
                            0,1,0,0,
                            -sin(angle),0,cos(angle),0,
                            0,0,0,1};
        CMatrix t = CMatrix(4, 4, factor);
        curMatrix = curMatrix * t;
    }

    else if (z>0) {
        double factor[] = { cos(angle),-sin(angle),0,0,
                            sin(angle),cos(angle),0,0,
                            0,0,1,0,
                            0,0,0,1};
        CMatrix t = CMatrix(4, 4, factor);
        curMatrix =  curMatrix * t;
    }
}

//V(Viewing)
void glViewport (GLint x, GLint y, GLsizei width, GLsizei height){
    cout << "V" << endl;
    _width = width;
    _height = height;
    double factor[] = {(double)width/2    ,0,0,(double)(width-1)/2+x,
                        0,(double)height/2,0,(double)(height-1)/2+y,
                        0,0,1,0,
                        0,0,0,1};
    CMatrix V = CMatrix(4,4,factor);
    V.Print();
    curMatrix = curMatrix* V;
}

//OP
void gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar){
    cout << "OP" << endl;

    double f = cos(fovy/2)/sin(fovy/2);
    double factor[] =  {f/aspect,0,0,0,
                        0,f,0,0,
                        0,0,(zFar+zNear)/(zNear-zFar),2*zNear*zFar/(zFar-zNear),
                        0,0,1,0};
    CMatrix V = CMatrix(4,4,factor);
    V.Normalize();
    V.Print();
    curMatrix = curMatrix* V;
    curMatrix.Print();
}

//MT(move to camera view)
void gluLookAt (GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ, GLdouble centerX, GLdouble centerY, GLdouble centerZ, GLdouble upX, GLdouble upY, GLdouble upZ){
    cout << "MT" << endl;
    double param[] = {1,0,0,-eyeX,
                      0,1,0,-eyeY,
                      0,0,1,-eyeZ,
                      0,0,0,1};
    CMatrix T = CMatrix(4, 4, param);
    
    //D
    double d1 = centerX - eyeX;
    double d2 = centerY - eyeY;
    double d3 = centerZ - eyeZ;
    double d  = sqrt( d1*d1 + d2*d2 + d3*d3 );
    d1 = d1/d ; d2 = d2/d; d3 = d3/d;
    
    //s
    double s1 = upY*d3 - d2*upZ;
    double s2 = upX*d3 - d1*upZ;
    double s3 = upX*d2 - d1*upY;
    double s  = sqrt( s1*s1 + s2*s2 + s3*s3 );
    s1 = s1/s ; s2 = s2/s; s3 = s3/s;


    //u
    double u1 = d2*s3 - d3*s2;
    double u2 = d1*s3 - d3*s1;
    double u3 = d1*s2 - d2*s1;
    
    double mparam[] =  {s1,s2,s3,0,
                        u1,u2,u3,0,
                        d1,d2,d3,0,
                        0,0,0,1};
    CMatrix M = CMatrix(4,4,mparam);
    
    M.Print();

    M = M*T;
    
   // curMatrix = curMatrix * T;
    curMatrix = curMatrix * M;

}

//base
void glutInit(int *argcp, char **argv){}
void glutInitDisplayMode(unsigned int mode){}
void glutInitWindowPosition(int x, int y) {}
void glutInitWindowSize(int width, int height) {_width = width;_height = height;}
int  glutCreateWindow(const char *title){return 1;}